package com.jessechanrui.template.pdfbox;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.contentstream.operator.Operator;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class PdfOperatorInspector {

    /**
     * 解析所有页面的操作符及其参数
     * @param pdfPath PDF 文件路径
     * @return 操作符日志列表
     * @throws IOException 加载 PDF 异常
     */
    public static List<String> extractAllOperators(String pdfPath) throws IOException {
        List<String> operatorLogs = new ArrayList<>();

        try (PDDocument document = Loader.loadPDF(new File(pdfPath))) {
            int pageCount = document.getNumberOfPages();

            for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
                PDPage page = document.getPage(pageIndex);
                PDFStreamParser parser = new PDFStreamParser(page);
                parser.parse();
                List<Object> tokens = parser.parse();

                for (int i = 0; i < tokens.size(); i++) {
                    Object token = tokens.get(i);
                    if (token instanceof Operator) {
                        Operator op = (Operator) token;
                        // if ("Do".equals(op.getName())) {

                        // 获取前面的参数
                        List<String> params = new ArrayList<>();
                        for (int j = i - 1; j >= 0; j--) {
                            Object prev = tokens.get(j);
                            if (prev instanceof COSBase) {
                                params.add(0, prev.toString());
                            } else {
                                break;
                            }
                        }

                        String log = String.format("Page %d - Operator: %s, Args: %s",
                                pageIndex + 1, op.getName(), String.join(", ", params));
                        operatorLogs.add(log);
                        // }
                    }
                }
            }
        }

        // 可选：打印全部内容
        System.out.println("======= PDF 操作符解析结果 =======");
        operatorLogs.forEach(System.out::println);
        System.out.println("======= 解析结束 =======");

        return operatorLogs;
    }

    /** 识别PDF文件里面的图片，包括正文图片和通过路径绘制的水印/图片水印
     * 分析pdf文件里面的 Do、gs、BDC  相关内容
     * 操作符: gs  参数: COSName{G3}-->说明页面中有一个图形状态（gs）设置了透明度（比如 0.3）
     * 操作符: Do  参数: COSName{Im0}-->然后通过 Do Im0 调用了一个图形对象 Im0，极有可能这是一个透明图层或图像类型的水印。
     * @param pdfPath
     * @throws IOException
     */
    public static Set<String> extractDoXObjectNames(String pdfPath) throws IOException {
        Set<String> xObjectNames = new HashSet<>();

        PDDocument document = Loader.loadPDF(new File(pdfPath));
        int pageCount = document.getNumberOfPages();

        for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
            PDPage page = document.getPage(pageIndex);
            PDFStreamParser parser = new PDFStreamParser(page);
            parser.parse();
            List<Object> tokens = parser.parse();

            for (int i = 0; i < tokens.size(); i++) {
                Object token = tokens.get(i);
                if (token instanceof Operator) {
                    Operator op = (Operator) token;
                    if ("Do".equals(op.getName())) {
                        // 获取前一个参数，一般为 COSName，例如 COSName{Im0}
                        Object prev = tokens.get(i - 1);
                        if (prev instanceof COSName) {
                            String xObjectName = ((COSName) prev).getName();
                            xObjectNames.add(xObjectName);
                        }
                    }
                }
            }
        }
        document.close();
        System.out.println("extractDoXObjectNames----"+xObjectNames);
        return xObjectNames;
    }



    /**
     * 用来分析操作符输出--根据字符输出来修改去除水印的逻辑
     * 主要是输出 DO,CM,GS,BOC操作符
     * @param pdfPath
     * @return
     * @throws IOException
     */
    public static Set<String> extractPartOperators(String pdfPath) throws IOException {
        Set<String> targetOperators = new HashSet<>();
        PDDocument document = Loader.loadPDF(new File(pdfPath));

        for (PDPage page : document.getPages()) {
            PDFStreamParser parser = new PDFStreamParser(page);
            parser.parse();
            List<Object> tokens = parser.parse();

            for (int i = 0; i < tokens.size(); i++) {
                Object token = tokens.get(i);
                if (token instanceof Operator) {
                    Operator op = (Operator) token;
                    String opName = op.getName();

                    // 仅处理 Do, cm, gs, BDC 操作符
                    if (opName.equals("Do") || opName.equals("cm") || opName.equals("gs") || opName.equals("BDC")) {
                        StringBuilder operatorInfo = new StringBuilder();

                        // 收集操作符的所有参数（向前查找）
                        List<Object> params = new ArrayList<>();
                        int j = i - 1;
                        while (j >= 0 && tokens.get(j) instanceof COSBase) {
                            params.add(0, tokens.get(j)); // 保持参数顺序
                            j--;
                        }

                        // 构建操作符信息字符串
                        for (Object param : params) {
                            operatorInfo.append(param).append(" ");
                        }
                        operatorInfo.append(opName);

                        targetOperators.add(operatorInfo.toString()+"\n");
                    }
                }
            }
        }

        document.close();
        System.out.println("extractPartOperators----"+targetOperators);
        return targetOperators;
    }

}

